package com.example.permissionsystem1_1.util.customClassLoaderUtil;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * 用于将自定义位置(指定的硬盘位置/指定的网络路径)的.class文件转成JVM的类对象的类加载器
 */
public class LoadCustomLocationClassLoader extends ClassLoader{
    /**
     * 字节码文件的根路径
     */
    private String classRootPath;

    public LoadCustomLocationClassLoader(String classRootPath) {
        this.classRootPath = classRootPath;
    }

    /**
     * 获取指定资源路径的类对象的方法
     * 由于要用到继承父类ClassLoader的成员方法findLoadedClass,所以不能做成静态的
     * @param name
     * @param model
     * @return
     * @throws ClassNotFoundException
     * @throws IOException
     */
//    @Override
    public Class<?> findClass(String name,String model) throws ClassNotFoundException, IOException {
        if(name==null||"".equals(name)){
            return null;
        }
        //检查这个类是否被其他加载器加载过
        Class cls=this.findLoadedClass(name);
        if(cls!=null){//已经加载过的类,直接返回已经存在的类对象
            return cls;
        }

        String classFullPath=classRootPath+name.replace(".", "/")+".class";
        byte[] data=null;
        if("url".equals(model)){//从网络的指定位置中获取.class文件,加载为JVM中的类对象
            URL url=new URL(classFullPath);
            HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
            data=getClassData(httpUrlConnection.getInputStream());
        }else if("local".equals(model)){//从本地硬盘的指定位置中获取.class文件,加载为JVM中的类对象
            //创建File
            File file=new File(classFullPath);
            data=getClassData(new FileInputStream(file));
        }else{//其他字符串不存在
            return null;
        }
        if(data==null){
            throw new ClassNotFoundException();
        }
        //转换成JVM中的对应的类对象
        Class<?> aClass = this.defineClass(name, data, 0, data.length);
        return aClass;
    }

    /**
     * 通过指定的输入流来读取指定位置的.class文件中的数据
     * @param in
     * @return
     * @throws IOException
     */
    private byte[] getClassData(InputStream in) throws IOException {
        //构造输入流
        BufferedInputStream bis=new BufferedInputStream(in);
        //创建一个内存流
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        //内存流读取输入流的内容
        byte[] bytes=new byte[1024*8];
        int len=-1;
        while ((len=bis.read(bytes))!=-1){
            bos.write(bytes,0,len);
            bos.flush();
        }
        byte[] bytes1 = bos.toByteArray();
        bos.close();
        bis.close();
        return bytes1;
    }


}
